//-----------------------------------------------------------------------
// <copyright file="TupleStoreSession.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the TupleStoreSession class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine.Tuples
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using ConflictResolution;
    using Exceptions;

    /// <summary>
    /// A TupleStore Session represents a single session of the TupleStore, and is associated with
    /// a single rule set.   Each invocation of a rule set is provided with a unique TupleStore
    /// Session.
    /// </summary>
    public class TupleStoreSession
    {
        /// <summary>
        /// A queue used to collect quantification monads for deferred processing.
        /// </summary>
        public Queue<TupleList> QuantificationQueue = new Queue<TupleList>();

        /// <summary>
        /// The agenda being used by the current session.
        /// </summary>
        public Agenda Agenda;

        /// <summary>
        /// A string used to uniquely identify the rule set for which this tupleStore session is used.
        /// The uniqueness of this identifier must be guaranteed by the executor.
        /// </summary>
        public string SessionIdentifier;

        /// <summary>
        /// A set of tupleStore contexts.
        /// </summary>
        private readonly Dictionary<string, TupleStoreContext> contextDictionary = new Dictionary<string, TupleStoreContext>();

        /// <summary>
        /// A collection of dictionaries of context-specific fact type counts.  Each fact type counts dictionary  
        /// stores a running count of the number of facts of any given type currently asserted to the tupleStore session.
        /// </summary>
        private readonly Dictionary<TupleStoreContext, Dictionary<Type, int>> contextFactTypeCounts = new Dictionary<TupleStoreContext, Dictionary<Type, int>>();

        /// <summary>
        /// A collection of context-specific dictionaries of entity tuples.   Each tuple dictionary stores 
        /// Entity Tuples keyed on the facts they represent. 
        /// </summary>
        private readonly Dictionary<TupleStoreContext, Dictionary<object, EntityTuple>> contextTupleDictionary = new Dictionary<TupleStoreContext, Dictionary<object, EntityTuple>>();

        /// <summary>
        /// The current tupleStore context.
        /// </summary>
        private TupleStoreContext context;

        /// <summary>
        /// Cached FactTypeCounts for the current context.
        /// </summary>
        private Dictionary<Type, int> contextFactTypeCountsCache;

        /// <summary>
        /// Number used to set id stamps on primary tuples.
        /// </summary>
        private long nextId = 1;

        /// <summary>
        /// Initializes a new instance of the TupleStoreSession class.
        /// </summary>
        /// <param name="sessionIdentifier">Unique identifier for the rule set which will use this tupleStore session.</param>
        public TupleStoreSession(string sessionIdentifier)
        {
            // No need to lock the session identifier
            this.SessionIdentifier = sessionIdentifier;

            // Create a new context automatically.
            this.CreateContext();
        }

        /// <summary>
        /// Gets the context token for the current context.
        /// </summary>
        public string ContextToken
        {
            get
            {
                return this.context.ContextToken;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the tupleStore session is initialised for a specific rule set.   Read-only.
        /// </summary>
        public bool IsInitializedForRuleSet
        {
            get
            {
                return !string.IsNullOrEmpty(this.SessionIdentifier) && this.context.EntityMap.Count > 0;
            }
        }

        /// <summary>
        /// Gets or sets a multicast delegate containing initial monadic functions
        /// used when primary tuples are first lifted into the TupleList monad.
        /// </summary>
        public MonadicFunction InitialMonadicFunctions
        {
            get
            {
                return this.context.InitialMonadicFunctions;
            }

            set
            {
                this.context.InitialMonadicFunctions = value;
            }
        }

        /// <summary>
        /// Gets the FactTypeCounts dictionary for the current context.
        /// </summary>
        private Dictionary<Type, int> FactTypeCounts
        {
            get
            {
                if (this.contextFactTypeCountsCache == null)
                {
                    var factTypeCounts = this.contextFactTypeCounts[this.context];
                    this.contextFactTypeCountsCache = factTypeCounts;
                    return factTypeCounts;
                }

                return this.contextFactTypeCountsCache;
            }
        }

        /// <summary>
        /// Gets the Tuple dictionary for the current context.
        /// </summary>
        private Dictionary<object, EntityTuple> TupleDictionary
        {
            get
            {
                return this.contextTupleDictionary[this.context];
            }
        }

        /// <summary>
        /// Creates a new context and returns a token (GUID) representing the context.  This
        /// method also performs a context switch, making the new context the current context.
        /// </summary>
        /// <returns>A token (GUID) representing the new context.</returns>
        public string CreateContext()
        {
            var contextToken = Guid.NewGuid().ToString();
            var newContext = new TupleStoreContext(contextToken, this);
            this.contextDictionary.Add(contextToken, newContext);

            if (this.SwitchContext(contextToken))
            {
                this.contextFactTypeCounts.Add(newContext, new Dictionary<Type, int>());
                this.contextTupleDictionary.Add(newContext, new Dictionary<object, EntityTuple>());
                return contextToken;
            }

            return TupleStoreExceptionHelper.ThrowTupleStoreExCtxSwchFailed();
        }

        /// <summary>
        /// Creates a new context and returns a token (GUID) representing the context.  This
        /// method can also perform a context switch, making the new context the current context.
        /// </summary>
        /// <param name="noSwitch">Set to true to prevent context switch.</param>
        /// <returns>A token (GUID) representing the new context.</returns>
        public string CreateContext(bool noSwitch)
        {
            if (noSwitch)
            {
                return this.CreateContext();
            }

            var contextToken = Guid.NewGuid().ToString();
            var newContext = new TupleStoreContext(contextToken, this);

            this.contextDictionary.Add(contextToken, newContext);
            this.contextFactTypeCounts.Add(newContext, new Dictionary<Type, int>());
            this.contextTupleDictionary.Add(newContext, new Dictionary<object, EntityTuple>());

            return contextToken;
        }

        /// <summary>
        /// Switches to a specified context.   The context is selected using a
        /// context token.
        /// </summary>
        /// <param name="contextToken">The context token for the context required.</param>
        /// <returns>True, if the switch was successfull.   False, otherwise.</returns>
        public bool SwitchContext(string contextToken)
        {
            TupleStoreContext ctxt;
            var outVal = false;

            if (this.contextDictionary.TryGetValue(contextToken, out ctxt))
            {
                this.context = ctxt;

                // Reset caches
                this.contextFactTypeCountsCache = null;

                outVal = true;
            }

            return outVal;
        }

        /// <summary>
        /// Removes a context.   If an attempt is made to remove the current
        /// context, this is only allowed if this is the only context remaining.
        /// </summary>
        /// <param name="contextToken">The token of the context to be removed.</param>
        /// <returns>True if the context was removed.   Otherwise, false.</returns>
        public bool RemoveContext(string contextToken)
        {
            TupleStoreContext ctxt;

            if (this.contextDictionary.TryGetValue(contextToken, out ctxt))
            {
                // If we are attempting to remove the current context, we can only
                // remove it if it is the last one.
                if (ctxt == this.context)
                {
                    if (this.contextDictionary.Count == 1)
                    {
                        this.contextDictionary.Clear();
                        this.contextFactTypeCounts.Clear();
                        this.contextTupleDictionary.Clear();
                        this.context = default(TupleStoreContext);

                        // Reset caches
                        this.contextFactTypeCountsCache = null;
                    }

                    return true;
                }

                this.contextDictionary.Remove(contextToken);
                this.contextFactTypeCounts.Remove(ctxt);
                this.contextTupleDictionary.Remove(ctxt);

                return true;
            }

            return false;
        }

        /// <summary>
        /// Registers a mapping between a fact type and a Entity Tuple type.
        /// </summary>
        /// <param name="factType">The fact type to be mapped to a Entity Tuple type.</param>
        /// <param name="entityTupleType">The Entity Tuple type to be mapped to a fact type.</param>
        public void RegisterEntityMap(Type factType, Type entityTupleType)
        {
            this.context.RegisterEntityMap(factType, entityTupleType);
        }

        /// <summary>
        /// De-registers an Entity Tuple for the given fact type.   Any facts of the
        /// given type are retracted from the tupleStore session.
        /// </summary>
        /// <param name="factType">A fact type.</param>
        public void DeregisterEntityMap(Type factType)
        {
            this.context.DeregisterEntityMap(factType);
        }

        /// <summary>
        /// Obtains a Entity Tuple for the given fact.   If the fact has not been
        /// asserted, it is asserted automatically.
        /// </summary>
        /// <param name="fact">The fact whose entity tuple is required.</param>
        /// <returns>An entity tuple for the given fact.</returns>
        public EntityTuple GetEntityTuple(object fact)
        {
            if (!(fact is ValueType) && fact == null)
            {
                return null;
            }

            EntityTuple pb;

            if (this.TupleDictionary.TryGetValue(fact, out pb))
            {
                return pb;
            }

            Type entityTupleType;

            if (this.context.EntityMap.TryGetValue(fact.GetType(), out entityTupleType))
            {
                this.Assert(fact);

                // TODO: This is surely wrong???
                return this.GetEntityTuple(fact);
            }

            return null;
        }

        /// <summary>
        /// Asserts a fact to the TupleStore session.
        /// </summary>
        /// <param name="fact">The fact to be asserted.</param>
        public void Assert(object fact)
        {
            var currentContext = this.context;

            foreach (var ctxt in this.contextDictionary.Values)
            {
                if (this.context != ctxt && !this.SwitchContext(ctxt.ContextToken))
                {
                    continue;
                }

                if (fact == null)
                {
                    continue;
                }

                var entityTupleNew = this.CreateEntityTuple(fact.GetType());

                if (entityTupleNew == null)
                {
                    continue;
                }

                entityTupleNew.Fact = fact;
                entityTupleNew.Id = this.GetNextId();
                this.AssertEntityTuple(entityTupleNew);
            }

            if (this.context == currentContext)
            {
                return;
            }

            this.SwitchContext(currentContext.ContextToken);
        }

        /// <summary>
        /// Asserts a fact to the current context.
        /// </summary>
        /// <typeparam name="TFact">The type of the fact to be asserted.</typeparam>
        /// <param name="fact">The fact to be asserted.</param>
        public void Assert<TFact>(TFact fact)
        {
            this.Assert(fact);
        }

        /// <summary>
        /// Asserts an array of facts to the TupleStore session.
        /// </summary>
        /// <param name="facts">The facts to be asserted.</param>
        public void Assert(object[] facts)
        {
            if (facts == null)
            {
                return;
            }

            foreach (var fact in facts)
            {
                this.Assert(fact);
            }
        }

        /// <summary>
        /// Asserts an array of facts to the TupleStore session.
        /// </summary>
        /// <typeparam name="TFact">The type of the fact contained in the array.</typeparam>
        /// <param name="facts">The facts to be asserted.</param>
        public void Assert<TFact>(TFact[] facts)
        {
            if (facts == null)
            {
                return;
            }

            foreach (var fact in facts)
            {
                this.Assert(fact);
            }
        }

        /// <summary>
        /// Retracts an entity tuple from the current context.
        /// </summary>
        /// <param name="fact">The fact contained in the Entity Tuple.</param>
        public void Retract(object fact)
        {
            if (fact == null)
            {
                return;
            }

            var currentContext = this.context;

            try
            {
                if (this.contextDictionary.Values.Where(ctxt => this.context == ctxt || this.SwitchContext(ctxt.ContextToken)).Count() > 0)
                {
                    EntityTuple tuple;

                    if (this.TupleDictionary.TryGetValue(fact, out tuple))
                    {
                        this.RetractEntityTuple(tuple);
                    }
                }
            }
            finally
            {
                if (this.context != currentContext)
                {
                    this.SwitchContext(currentContext.ContextToken);
                }
            }
        }

        /// <summary>
        /// Retracts an entity tuple from the current context.
        /// </summary>
        /// <typeparam name="TFact">The type of the fact contained in the Entity Tuple to be retracted.</typeparam>
        /// <param name="fact">The fact contained in the Entity Tuple.</param>
        public void Retract<TFact>(TFact fact)
        {
            var currentContext = this.context;

            try
            {
                if (this.contextDictionary.Values.Where(ctxt => this.context == ctxt || this.SwitchContext(ctxt.ContextToken)).Count() > 0)
                {
                    EntityTuple tuple;

                    if (this.TupleDictionary.TryGetValue(fact, out tuple))
                    {
                        this.RetractEntityTuple(tuple);
                    }
                }
            }
            finally
            {
                if (this.context != currentContext)
                {
                    this.SwitchContext(currentContext.ContextToken);
                }
            }
        }
        
        /// <summary>
        /// Retracts an array of entity tuples from the current context.
        /// </summary>
        /// <param name="facts">The facts to be retracted.</param>
        public void Retract(object[] facts)
        {
            if (facts == null)
            {
                return;
            }

            foreach (var fact in facts)
            {
                this.Retract(fact);
            }
        }

        /// <summary>
        /// Retracts an array of facts from the TupleStore session.
        /// </summary>
        /// <typeparam name="TFact">The type of the facts to be retracted.</typeparam>
        /// <param name="facts">The facts to be retracted.</param>
        public void Retract<TFact>(TFact[] facts)
        {
            if (facts == null)
            {
                return;
            }

            foreach (var fact in facts)
            {
                this.Retract(fact);
            }
        }

        /// <summary>
        /// Checks for the existence in the tupleStore session of a given fact.
        /// </summary>
        /// <param name="fact">The fact whose existence is to be checked.</param>
        /// <returns>True, if the fact exists in the tupleStore session.   Otherwise, false.</returns>
        public bool FactExists(object fact)
        {
            if (fact == null)
            {
                return false;
            }

            EntityTuple tuple;
            return this.TupleDictionary.TryGetValue(fact, out tuple);
        }

        /// <summary>
        /// Checks for the existence of facts of a given type.
        /// </summary>
        /// <param name="factType">The fact type to be tested for.</param>
        /// <returns>True, if fact type exists, else false.</returns>
        public bool FactTypeExists(Type factType)
        {
            if (factType == null)
            {
                return false;
            }

            int count;
            this.FactTypeCounts.TryGetValue(factType, out count);
            return count > 0;
        }

        /// <summary>
        /// Returns a count of the number of facts of a given type currently
        /// asserted to the Fact Fase.
        /// </summary>
        /// <param name="factType">The fact type for which a count is required.</param>
        /// <returns>
        /// The count of the number of facts of the given type currently asserted
        /// to working memory.
        /// </returns>
        public int FactTypeCount(Type factType)
        {
            if (factType == null)
            {
                return 0;
            }

            int count;
            this.FactTypeCounts.TryGetValue(factType, out count);
            return count;
        }

        /// <summary>
        /// Retract all facts for the current context.
        /// </summary>
        public void RetractAll()
        {
            TupleStore.Clear(this);

            foreach (var tuple in this.TupleDictionary.Values)
            {
                this.ReleaseTuples(tuple);
            }

            this.TupleDictionary.Clear();
            this.FactTypeCounts.Clear();
        }

        /// <summary>
        /// Retract all facts of a given type for the current context.
        /// </summary>
        /// <param name="factType">The type of the facts to be removed.</param>
        public void RetractAllOfType(Type factType)
        {
            if (factType == null)
            {
                return;
            }

            foreach (var factItem in this.TupleDictionary)
            {
                if (factItem.Key.GetType() != factType)
                {
                    continue;
                }

                this.ReleaseTuples(factItem.Value);
                this.TupleDictionary.Remove(factItem.Key);
            }

            this.FactTypeCounts.Remove(factType);
        }

        /// <summary>
        /// Resets the current context in the tuple session.   Retracts all facts and de-registers all 
        /// Entity Tuple mappings.
        /// </summary>
        public void Reset()
        {
            this.RetractAll();
            this.context.EntityMap.Clear();
            this.SessionIdentifier = string.Empty;
        }

        /// <summary>
        /// Clears all facts and tuples, but does not de-register the entity tuples.  
        /// It is supported for consistency, and used when returning a tupleStore session to a pool.
        /// </summary>
        public void Clear()
        {
            // Clear the tuples
            TupleStore.Clear(this);

            this.TupleDictionary.Clear();

            // Clear the fact type counts
            this.contextFactTypeCountsCache = null;

            // Clear the fact / tuple maps
            foreach (var fbm in this.contextTupleDictionary.Values)
            {
                fbm.Clear();
            }
        }

        /// <summary>
        /// Returns the next ID.
        /// </summary>
        /// <returns>The next fact ID.</returns>
        private long GetNextId()
        {
            // TODO:  This method needs to be expanded to support random as well as sequential ID creation.
            //        For random, we will need to detect duplicates.    The logic will be driven by some
            //        for of conflict resolution plan.
            return this.nextId++;
        }

        /// <summary>
        /// Returns a new EntityTuple of a given type.
        /// </summary>
        /// <param name="factType">A Type instance representing the type of fact to be contained in Entity Tuple.</param>
        /// <returns>An uninitilised Entity Tuple object.</returns>
        /// <remarks>
        /// When registering a fact in the tupleStore session, the client code performs the following
        /// sequence:
        /// a) Obtains a Entity Tuple of the required type from the Tuple Pool via
        ///    a call to GetEntityTuple()
        /// b) Initialises the Entity Tuple, adding the fact to it.  The client performs tuple initialisation.
        /// c) Asserts the tuple to the tupleStore session by calling Assert().
        /// </remarks>
        private EntityTuple CreateEntityTuple(Type factType)
        {
            if (factType == null)
            {
                TupleStoreExceptionHelper.ThrowTupleStoreExFactTypeIsNull();
                return null;
            }

            Type entityTupleType;

            if (this.context.EntityMap.TryGetValue(factType, out entityTupleType))
            {
                // Allow casting errors to be thrown if registration is incorrect
                return (EntityTuple)TuplePool.GetItem(entityTupleType);
            }

            return null;
        }

        /// <summary>
        /// Asserts a Entity Tuple to the TupleStore Session.
        /// </summary>
        /// <param name="tuple">The Entity Tuple to be asserted.</param>
        private void AssertEntityTuple(EntityTuple tuple)
        {
            if (tuple == null || tuple.Fact == null)
            {
                TupleStoreExceptionHelper.ThrowTupleStorePbUninitAssert();
                return;
            }

            EntityTuple existingTuple;

            if (!this.TupleDictionary.TryGetValue(tuple.Fact, out existingTuple))
            {
                this.TupleDictionary.Add(tuple.Fact, tuple);

                // Register fact type in the Fact Type Counts dictionary
                int count;

                if (!this.FactTypeCounts.TryGetValue(tuple.Fact.GetType(), out count))
                {
                    this.FactTypeCounts.Add(tuple.Fact.GetType(), 0);
                }

                this.FactTypeCounts[tuple.Fact.GetType()] = ++count;
                return;
            }

            // Tuple already exists in this context.  Retract and reassert.
            this.Retract(tuple);
            this.Assert(tuple);
        }

        /// <summary>
        /// Retracts a Entity Tuple for the current context.
        /// </summary>
        /// <param name="tuple">The Entity Tuple to be retracted.</param>
        private void RetractEntityTuple(EntityTuple tuple)
        {
            if (tuple == null || tuple.Fact == null)
            {
                TupleStoreExceptionHelper.ThrowTupleStorePbUninitRetract();
                return;
            }

            if (!this.TupleDictionary.Remove(tuple.Fact))
            {
                TupleStoreExceptionHelper.ThrowTupleStorePbNotRegRetract();
            }

            this.DecrementTypeCount(tuple.Fact.GetType());
            this.ReleaseTuples(tuple);
        }

#pragma warning disable 1587
#if FULL_ASYMMETRIC_RETRACT
        /// <summary>
        /// Release a tuple back to the Tuple Pool.   The method releases all inner tuples
        /// recursively.
        /// </summary>
        /// <param name="tuple">Tuple to be released</param>
        private void ReleaseTuples(Tuple tuple)
        {
            if (tuple != null)
            {
                while (tuple.SubsumerTuplesHead.NextIndex > 0)
                {
                    var nextTuple = tuple.SubsumerTuplesHead.NextTuple;

                    var entIdx = tuple.SubsumerTuplesHead.NextIndex - 1;

                    for (var idx = 0; idx < nextTuple.SubsumerEntries.Length; idx++)
                    {
                        if (idx != entIdx)
                        {
                            // Remove from other chain, if required
                            var subEntPrevIndex = nextTuple.SubsumerEntries[idx].PrevIndex;
                            var subEntPrevTuple = nextTuple.SubsumerEntries[idx].PrevTuple;

                            if (subEntPrevTuple != null && subEntPrevIndex == 0)
                            {
                                subEntPrevTuple.SubsumerTuplesHead = nextTuple.SubsumerEntries[idx];
                            }
                            else if (subEntPrevIndex > 0)
                            {
                                var subEntPrevIdx = subEntPrevIndex - 1;
                                subEntPrevTuple.SubsumerEntries[subEntPrevIdx].NextIndex = nextTuple.SubsumerEntries[idx].NextIndex;
                                subEntPrevTuple.SubsumerEntries[subEntPrevIdx].NextTuple = nextTuple.SubsumerEntries[idx].NextTuple;
                            }

                            nextTuple.SubsumerEntries[idx].PrevIndex = nextTuple.SubsumerEntries[idx].NextIndex = 0;
                        }
                    }

                    // Remove from this chain
                    tuple.SubsumerTuplesHead.NextIndex = nextTuple.SubsumerEntries[entIdx].NextIndex;
                    tuple.SubsumerTuplesHead.NextTuple = nextTuple.SubsumerEntries[entIdx].NextTuple;

                    var subHeadNextIndex = tuple.SubsumerTuplesHead.NextIndex;
                    var subHeadNextTuple = tuple.SubsumerTuplesHead.NextTuple;

                    if (subHeadNextIndex > 0)
                    {
                        var subHeadNextIdx = subHeadNextIndex - 1;
                        subHeadNextTuple.SubsumerEntries[subHeadNextIdx].PrevIndex = 0;
                        subHeadNextTuple.SubsumerEntries[subHeadNextIdx].PrevTuple = tuple;
                    }

                    nextTuple.SubsumerEntries[entIdx].PrevIndex = nextTuple.SubsumerEntries[entIdx].NextIndex = 0;
                    ReleaseTuples(nextTuple);
                }

                if (tuple.AgendaTimeStamp >= 0)
                {
                    // This tuple is bound to a production on the agenda
                    if (this._agenda != null)
                    {
                        this._agenda.Remove(tuple);
                        tuple.AgendaTimeStamp = -1;
                    }
                }

                TuplePool.ReturnItem(tuple);
            }
        }

        /// <summary>
        /// Release a tuple back to the Tuple Pool.   The method releases all inner tuples
        /// recursively.
        /// </summary>
        /// <param name="tuple">Tuple to be released</param>
        private void ReleaseTuples(EntityTuple tuple)
        {
            if (tuple != null)
            {
                while (tuple.SubsumerTuplesHead.NextIndex > 0)
                {
                    var nextTuple = tuple.SubsumerTuplesHead.NextTuple;
                    var entIdx = tuple.SubsumerTuplesHead.NextIndex - 1;

                    for (var idx = 0; idx < nextTuple.SubsumerEntries.Length; idx++)
                    {
                        if (idx != entIdx)
                        {
                            // Remove from other chain, if required
                            var subEntPrevIndex = nextTuple.SubsumerEntries[idx].PrevIndex;
                            var subEntPrevTuple = nextTuple.SubsumerEntries[idx].PrevTuple;

                            if (subEntPrevTuple != null && subEntPrevIndex == 0)
                            {
                                subEntPrevTuple.SubsumerTuplesHead = nextTuple.SubsumerEntries[idx];
                            }
                            else if (subEntPrevIndex > 0)
                            {
                                var subEntPrevIdx = subEntPrevIndex - 1;
                                subEntPrevTuple.SubsumerEntries[subEntPrevIdx].NextIndex = nextTuple.SubsumerEntries[idx].NextIndex;
                                subEntPrevTuple.SubsumerEntries[subEntPrevIdx].NextTuple = nextTuple.SubsumerEntries[idx].NextTuple;
                            }

                            nextTuple.SubsumerEntries[idx].PrevIndex = nextTuple.SubsumerEntries[idx].NextIndex = 0;
                        }
                    }

                    // Remove from this chain
                    tuple.SubsumerTuplesHead.NextIndex = nextTuple.SubsumerEntries[entIdx].NextIndex;
                    tuple.SubsumerTuplesHead.NextTuple = nextTuple.SubsumerEntries[entIdx].NextTuple;

                    var subHeadNextIndex = tuple.SubsumerTuplesHead.NextIndex;
                    var subHeadNextTuple = tuple.SubsumerTuplesHead.NextTuple;

                    if (subHeadNextIndex > 0)
                    {
                        var subHeadNextIdx = subHeadNextIndex - 1;
                        subHeadNextTuple.SubsumerEntries[subHeadNextIdx].PrevIndex = 0;
                        subHeadNextTuple.SubsumerEntries[subHeadNextIdx].PrevTuple = tuple;
                    }

                    nextTuple.SubsumerEntries[entIdx].PrevIndex = nextTuple.SubsumerEntries[entIdx].NextIndex = 0;
                    ReleaseTuples(nextTuple);
                }

                TuplePool.ReturnItem(tuple);
            }
        }
#else
        /// <summary>
        /// Release a tuple back to the Tuple Pool.   The method releases all inner tuples
        /// recursively.
        /// </summary>
        /// <param name="tuple">Tuple to be released.</param>
        private void ReleaseTuples(Tuple tuple)
        {
            if (tuple == null)
            {
                return;
            }

            while (tuple.SubsumerTuples.Count > 0)
            {
                var innerTuple = tuple.SubsumerTuples.Dequeue();

                if (!innerTuple.IsPooled)
                {
                    this.ReleaseTuples(innerTuple);
                }
            }

            if (tuple.AgendaTimestamp >= 0)
            {
                // This tuple is bound to a production on the agenda
                if (this.Agenda != null)
                {
                    this.Agenda.Remove(tuple);
                    tuple.AgendaTimestamp = -1;
                }
            }

            // Tuples are disposed when returned to the pool
            if (!tuple.IsPooled)
            {
                TuplePool.ReturnItem(tuple);
            }
        }

        /// <summary>
        /// Release an entity tuple back to the Tuple Pool.   The method releases all inner tuples
        /// recursively.
        /// </summary>
        /// <param name="tuple">Tuple to be released.</param>
        private void ReleaseTuples(EntityTuple tuple)
        {
            if (tuple == null)
            {
                return;
            }

            while (tuple.SubsumerTuples.Count > 0)
            {
                var innerTuple = tuple.SubsumerTuples.Dequeue();
                this.ReleaseTuples(innerTuple);
            }

            // Tuples are disposed when returned to the pool
            if (!tuple.IsPooled)
            {
                TuplePool.ReturnItem(tuple);
            }
        }
#endif
#pragma warning restore 1587

        /// <summary>
        /// Decrements the fact type count.
        /// </summary>
        /// <param name="factType">The fact type whose count is to be decremented.</param>
        private void DecrementTypeCount(Type factType)
        {
            if (factType == null)
            {
                return;
            }

            this.FactTypeCounts[factType] = this.FactTypeCounts[factType] - 1;
        }

        /// <summary>
        /// A TupleStore Context provides a context in which a rule set is executed.   A rule set 
        /// may perform context switches as required.  For example, the TupleStore Context maintains 
        /// a specific set of mappings between fact types and Entity Tuple types.   Different
        /// parts of a rule set may therefore use different tuples for the same facts.
        /// </summary>
        internal class TupleStoreContext
        {
            /// <summary>
            /// Gets the context token for this context.
            /// </summary>
            public readonly string ContextToken;

            /// <summary>
            /// Multicast delegate containing initial monadic functions
            /// used when primary tuples are first lifted into the
            /// TupleList monad.
            /// </summary>
            public MonadicFunction InitialMonadicFunctions;

            /// <summary>
            /// Gets the entity map.
            /// </summary>
            public Dictionary<Type, Type> EntityMap;

            /// <summary>
            /// TupleStore session with which this context is associated.
            /// </summary>
            private readonly TupleStoreSession session;

            /// <summary>
            /// Initializes a new instance of the TupleStoreContext class.
            /// </summary>
            /// <param name="contextToken">A string token representing the current context.</param>
            /// <param name="session">A tuple store session.</param>
            public TupleStoreContext(string contextToken, TupleStoreSession session)
            {
                this.EntityMap = new Dictionary<Type, Type>();
                this.ContextToken = contextToken;
                this.session = session;
            }

            /// <summary>
            /// Registers a mapping between a fact type and a Entity Tuple type.
            /// </summary>
            /// <param name="factType">The fact type to be mapped to a Entity Tuple type.</param>
            /// <param name="entityTupleType">The Entity Tuple type to be mapped to a fact type.</param>
            public void RegisterEntityMap(Type factType, Type entityTupleType)
            {
                Type existingEntityTupleType;

                if (this.EntityMap.TryGetValue(factType, out existingEntityTupleType))
                {
                    return;
                }

                this.EntityMap.Add(factType, entityTupleType);
            }

            /// <summary>
            /// De-registers a Entity Tuple for the given fact type.   Any facts of the
            /// given type are retracted from the tupleStore session.
            /// </summary>
            /// <param name="factType">A fact type.</param>
            public void DeregisterEntityMap(Type factType)
            {
                if (factType == null)
                {
                    return;
                }

                // Retract ???
                this.session.RetractAllOfType(factType);
                this.EntityMap.Remove(factType);
                this.session.FactTypeCounts.Remove(factType);
            }
        }
    }
}